package net.butfly.albacore.support;

import java.io.Serializable;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import net.butfly.albacore.utils.ByteUtils;
import net.butfly.albacore.utils.EnumUtils;
import net.butfly.albacore.utils.GenericUtils;

@SuppressWarnings("unused")
public abstract class CloneSupport<T extends CloneSupport<T>> implements Cloneable, Serializable {
	private static final long serialVersionUID = 2368563230852816358L;
	public final static int METHOD_GET = 0;
	public final static int METHOD_SET = 1;

	@SuppressWarnings("unchecked")
	public T clone() {
		try {
			return (T) super.clone();
		} catch (CloneNotSupportedException e) {
			return null;
		}
	}

	public <D extends CloneSupport<D>> D clone(Class<D> clazz) {
		D dst = null;
		try {
			dst = clazz.newInstance();
		} catch (InstantiationException ex) {
			return null;
		} catch (IllegalAccessException ex) {
			return null;
		}
		copyByProperties(dst, this);
		return dst;
	}

	public CloneSupport<T> copy(CloneSupport<?> src) {
		if (null != src) copyByProperties(this, src);
		return this;
	}

	public Map<String, Object> toMap() {
		Map<String, Object> map = new HashMap<String, Object>();
		Map<String, Method[]> properties = GenericUtils.getAllProperties(this.getClass());
		for (String name : properties.keySet()) {
			try {
				Method getter = properties.get(name)[METHOD_GET];
				if (null != getter) {
					Object v = getter.invoke(this);
					if (null != v) {
						map.put(name, v);
					}
				}
			} catch (IllegalArgumentException e) {} catch (IllegalAccessException e) {} catch (InvocationTargetException e) {}
		}
		return map;
	}

	@SuppressWarnings("unchecked")
	public static <D extends CloneSupport<D>> D[] copy(CloneSupport<?>[] src, Class<D> clazz) {
		if (null == src) return null;
		D[] r = (D[]) Array.newInstance(clazz, src.length);
		for (int i = 0; i < src.length; i++) {
			r[i] = src[i].clone(clazz);
		}
		return r;
	}

	private static void copyByField(CloneSupport<?> dst, CloneSupport<?> src) {
		Map<String, Field> dmap = GenericUtils.getAllFields(dst.getClass());
		Map<String, Field> smap = GenericUtils.getAllFields(src.getClass());
		for (String name : smap.keySet()) {
			Field fsrc = smap.get(name);
			try {
				Object value = fsrc.get(src);
				if (null == value) {
					continue;
				}
				Field fdst = dmap.get(name);
				if (null == fdst) {
					continue;
				}
				fdst.set(dst, castValue(value, fdst.getType(), fsrc.getType()));
			} catch (IllegalArgumentException ex) {} catch (IllegalAccessException ex) {} catch (SecurityException ex) {}
		}
	}

	private static void copyByProperties(CloneSupport<?> dst, CloneSupport<?> src) {
		Map<String, Method[]> dmap = GenericUtils.getAllProperties(dst.getClass());
		Map<String, Method[]> smap = GenericUtils.getAllProperties(src.getClass());
		for (String name : smap.keySet()) {
			try {
				Method fsrc = smap.get(name)[METHOD_GET];
				if (null == fsrc) continue;
				Object value = fsrc.invoke(src);
				if (null == value) continue;

				Method[] fs = dmap.get(name);
				if (null == fs) {
					continue;
				}
				Method fdst = fs[METHOD_SET];
				if (null == fdst) {
					continue;
				}
				fdst.invoke(dst, castValue(value, fdst.getParameterTypes()[0], fsrc.getReturnType()));
			} catch (IllegalArgumentException ex) {} catch (IllegalAccessException ex) {} catch (SecurityException ex) {} catch (InvocationTargetException e) {}
		}
	}

	@SuppressWarnings("unchecked")
	private static Object castValue(Object value, Class<?> setClass, Class<?> getClass) {
		// 枚举类型和integer的互换
		if (isEnum(setClass) && isInteger(getClass)) {
			return EnumUtils.valueOf((Class<EnumSupport<?>>) setClass, ((Number) value).intValue());
		} else if (isInteger(setClass) && isEnum(getClass)) {
			return ((EnumSupport<?>) value).value();
			// String和byte[]的互换
		} else if (isBytes(setClass) && isString(getClass)) {
			return ByteUtils.hex2byte((String) value);
		} else if (isString(setClass) && isBytes(getClass)) {
			return ByteUtils.byte2hex((byte[]) value);
			// integer和 String的互换
		} else if (isString(setClass) && isInteger(getClass)) {
			return Integer.toString(((Number) value).intValue());
		} else if (isInteger(setClass) && isString(getClass)) {
			return Integer.parseInt(value.toString());
		} else { // TODO: deeply copy, be done later.
			// 其他直接转换
			return value;
		}

	}

	private static boolean isInteger(Class<?> clazz) {
		return int.class.equals(clazz) || Integer.class.equals(clazz);
	}

	private static boolean isString(Class<?> clazz) {
		return String.class.equals(clazz);
	}

	private static boolean isBytes(Class<?> clazz) {
		return byte[].class.equals(clazz);
	}

	private static boolean isEnum(Class<?> clazz) {
		return EnumSupport.class.isAssignableFrom(clazz);
	}
}
